﻿using System.Reflection;

namespace Devonline.AspNetCore;

/// <summary>
/// Excel 数据结构
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExcelData<T>
{
    public ExcelData()
    {
        Comparer = new MemberEqualityComparer<T>();
        Attributes = new List<ExcelAttribute>();
    }

    /// <summary>
    /// Excel 文件名
    /// </summary>
    public string? FileName { get; set; }
    /// <summary>
    /// Excel 当前表格名称
    /// </summary>
    public string? SheetName { get; set; }
    /// <summary>
    /// Excel 当前表格序号
    /// Epplus 中 Sheet Index 序号从 0 开始
    /// </summary>
    public int SheetIndex { get; set; }
    /// <summary>
    /// 比较器, 用于确定两个当前类型的对象是否相等
    /// 默认使用 MemberEqualityComparer 作为比较器, 即比较全部字段的值不可重复
    /// </summary>
    public IEqualityComparer<T>? Comparer { get; set; }
    /// <summary>
    /// 导入或导出的列名
    /// </summary>
    public IEnumerable<string>? Columns { get; set; }
    /// <summary>
    /// 针对 Excel 当前导入对象的字段自定义设置方法
    /// 使用了自定义设置方法, 则不在执行默认设置规则
    /// 字典的键为字段名, 字典的值两个传入参数为: 当前对象和当前列 excel 单元格中读取的值
    /// 导入时应通过 Converters 方法直接设置当前对象的属性值
    /// 导出时使用返回值以设置不同类型的输出值
    /// 设置失败请抛出异常
    /// </summary>
    public IDictionary<string, Func<T, object?, object?>>? Converters { get; set; }
    /// <summary>
    /// 行校验器, 当读取完一整行后执行, 用于数据判断重复等自定义校验
    /// 两个输入参数为当前读取的对象和已读取的所有对象列表
    /// 校验失败请抛出异常
    /// </summary>
    public Action<T, IEnumerable<T>>? Validator { get; set; }
    /// <summary>
    /// 导入或导出的数据
    /// </summary>
    public IEnumerable<T>? Data { get; set; }
    /// <summary>
    /// 导入或导出时的错误信息
    /// </summary>
    public IEnumerable<KeyValuePair<int, string>>? Messages { get; set; }
    /// <summary>
    /// 当前类型设置的 Excel 特性列表
    /// 取值优先级: Columns -> ExcelAttribute
    /// </summary>
    public IEnumerable<ExcelAttribute> Attributes { get; set; }

    /// <summary>
    /// 获取导出的错误消息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="excelData"></param>
    /// <returns></returns>
    public string GetErrorMessage()
    {
        var error = string.Empty;
        if (Messages != null && Messages.Any())
        {
            error += typeof(T).GetDisplayName() + @"<br \>";
            foreach (var err in Messages)
            {
                error += $@"第 {err.Key} 行: {err.Value} <br \>";
                if (error.Length >= 300)
                {
                    error += @"<br \> ...";
                    break;
                }
            }
        }

        return error;
    }
    /// <summary>
    /// 获取到字段的特性
    /// </summary>
    /// <returns>ExportExcelAttribute集合</returns>
    public IEnumerable<ExcelAttribute> GetAttributes()
    {
        var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        if (Columns == null)
        {
            var excelPropertyInfos = propertyInfos.Where(x => x.HasAttribute<ExcelAttribute>());
            Columns ??= excelPropertyInfos.Any() ? excelPropertyInfos.Select(x => x.Name) : propertyInfos.Select(x => x.Name);
        }

        var index = AppSettings.UNIT_ONE;
        var attributes = new List<ExcelAttribute>();
        foreach (var column in Columns)
        {
            var propertyInfo = propertyInfos.FirstOrDefault(x => x.Name == column);
            if (propertyInfo != null)
            {
                var attribute = propertyInfo.GetExcelAttribute();
                if (attribute != null)
                {
                    if (attribute.Index <= AppSettings.UNIT_ZERO)
                    {
                        attribute.Index = index++;
                    }

                    attributes.Add(attribute);
                }
            }
        }

        Attributes = attributes;
        return Attributes;
    }
}